package cn.billho.popcache.starter.aop;

import cn.billho.popcache.annotation.GlobalLock;
import cn.billho.popcache.core.api.IGlobalLockApi;
import cn.billho.popcache.core.codec.IResultParse;
import cn.billho.popcache.core.key.IKeyGenerate;
import cn.hutool.core.lang.UUID;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.lang.reflect.Method;
import java.lang.reflect.Type;

@Aspect
@Service
@Slf4j
public class GlobalLockAspect {

    @Autowired
    IGlobalLockApi globalLockApi;

    @Autowired
    IKeyGenerate keyGenerate;

    @Autowired
    IResultParse resultParse;

    @Pointcut("@annotation(cn.billho.popcache.annotation.GlobalLock)")
    public void aspect() {
    }

    @Around("aspect()&&@annotation(anno)")
    public Object interceptor(ProceedingJoinPoint invocation, GlobalLock anno) throws Throwable{
        MethodSignature signature = (MethodSignature) invocation.getSignature();
        Method method = signature.getMethod();
        Class<?>[] parameterTypes = method.getParameterTypes();
        Object[] arguments = invocation.getArgs();

        String key = keyGenerate.buildKey(anno.key(),parameterTypes,arguments);

        String val = method.getName()+ ":" + UUID.fastUUID().toString(true);
        Object result;
        if (globalLockApi.acquireLock(key, val, anno.expireSeconds())){
            result = invocation.proceed();
            // release log
            globalLockApi.releaseLock(key,val);
        }
        else{
            log.warn("acquireLock fail , key is =>" + key);
            Type returnType = method.getGenericReturnType();
            result = resultParse.parse(null,returnType,null);
        }

        return result;
    }
}
